package apps;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;
import java.util.StringTokenizer;

import spread.SpreadConnection;
import spread.SpreadException;
import spread.SpreadGroup;
import spread.SpreadMessage;

public class AccountClient {

    private SpreadConnection connection;
    private String connName;
    private String groupName;
    private Account account;
    private boolean allConnected = false;
    private int numReplicas = 3;

    public void init(String server, String groupName, int numReplicas) {
        //here we will connect to the spread server and join our group
        account = new Account(); //instantiate a new account object with 0 amount
        this.groupName = groupName;
        Random randomGenerator = new Random();
        int randomInt = randomGenerator.nextInt(100);
        connName = randomInt + "ClientConn";
        // System.out.println(connName);
        this.numReplicas = numReplicas;
        setConnection(new SpreadConnection());
        ListenerThread listeningThread = new ListenerThread(this); //advanced listener we have made to catch the messages incoming
        try {
            getConnection().add(listeningThread); //add the listener to this connection
            getConnection().connect(InetAddress.getByName(server), 4333, connName,
                    false, true); //connect to server

            SpreadGroup group = new SpreadGroup();
            group.join(getConnection(), groupName); //join group
            
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SpreadException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void receiveAndProcessMembershipMessages(SpreadMessage message) {

        if (message.getMembershipInfo().getMembers().length >= numReplicas) { //check if all that are meant to connect are connected
            allConnected = true;
        }
    }

    public void showBalance() {
        System.out.println("Balance is: " + account.getBalance());
    }

    public void performDeposit(double value) { //commit a deposit operation
        account.deposit(value);
        sendMessage("deposit " + value); //push the update to other clients
    }

    public void performInterest(double percentage) { //commit an interest operation
        account.addInterest(percentage);
        sendMessage("addInterest " + percentage); //push the update to other clients
    }

    public void sleep(long delay) { //sleep used in batch processing
        try {
            Thread.sleep(delay*1000); //in seconds
        } catch (Exception ex) {
        }
    }

    public void processMessageReceived(SpreadMessage message) { //process the normal message received on the connection
        try {
            String msg = new String(message.getData());
            System.out.println("Received remote message from " + message.getSender() + ": " + msg);
            System.out.println(">>>");
            StringTokenizer tokenizer = new StringTokenizer(msg.trim());
            String command = tokenizer.nextToken();
            String value = tokenizer.nextToken();
            if (command.equals("deposit")) {
                account.deposit(Double.parseDouble(value));
            } else if (command.equals("addInterest")) {
                account.addInterest(Double.parseDouble(value));
            }
            //}
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void processMessageLocal(String message) { //process message received from command line
        try {
            StringTokenizer tokenizer = new StringTokenizer(message.trim());
            String command = tokenizer.nextToken();
            if (command.equals("deposit")) {
                String value = tokenizer.nextToken();
                performDeposit(Double.parseDouble(value));
            } else if (command.equals("addInterest")) {
                String value = tokenizer.nextToken();
                performInterest(Double.parseDouble(value));
            } else if (command.equals("sleep")) {
                String value = tokenizer.nextToken();
                sleep(Long.parseLong(value));
            } else if (command.equals("exit")) {
                System.exit(0);
            } else if (command.equals("balance")) {
                showBalance();
            } else {
                System.out.println("Unknown command " + message
                        + ". Try again.");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void processFile(String fileName) throws IOException,
            FileNotFoundException, SpreadException { //process the file that contains batch commands
        FileInputStream fileStream = new FileInputStream(fileName);
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                fileStream));
        String newLine = null;
        while ((newLine = reader.readLine()) != null) {
            System.out.println(">>> " + newLine);
            processMessageLocal(newLine);
   
        }
    }

    public void sendMessage(String text) {
        SpreadMessage message = new SpreadMessage();
        message.setData(text.getBytes());
        message.addGroup(groupName);
        message.setReliable(); //we want a reliable and safe message
        message.setSafe();
        message.setSelfDiscard(true); //we don't need to send the message to ourselves also
        try {
            getConnection().multicast(message);
        } catch (SpreadException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public boolean areAllConnected() {
        return allConnected;
    }

    public static void main(String args[]) throws IOException,
            FileNotFoundException, SpreadException {
        String serverName = "fredrikstad.ifi.uio.no";//if no arguments are supplied we give some default values
        String groupName = "EndriSerhatFabrice";
        int numReplicas = 1;
        if (args.length >= 3) {
            serverName = args[0];
            groupName = args[1];
            numReplicas = Integer.parseInt(args[2]);

        }
        AccountClient accountClient = new AccountClient();
        accountClient.init(serverName, groupName, numReplicas); //start the client

        InputStreamReader streamReader = new InputStreamReader(System.in);
        BufferedReader bufferedReader = new BufferedReader(streamReader);
        System.out.println("Waiting...");
        while (!accountClient.areAllConnected()) {
            Thread.yield();
        }
        System.out.println("All connected. Can proceed.");
        if (args.length == 4) {
            System.out.println("Reading from file...");
            accountClient.processFile(args[3]);
        } else {
            System.out.println("Console input:");
            while (true) {
                System.out.print(">>>");
                accountClient.processMessageLocal(bufferedReader.readLine());
            }
        }
    }

    /**
     * @return the connection
     */
    public SpreadConnection getConnection() {
        return connection;
    }

    /**
     * @param connection the connection to set
     */
    public void setConnection(SpreadConnection connection) {
        this.connection = connection;
    }
}
